/***************************************************************************************
* Copyright (c) 2014-2022 Zihao Yu, Nanjing University
*
* NEMU is licensed under Mulan PSL v2.
* You can use this software according to the terms and conditions of the Mulan PSL v2.
* You may obtain a copy of Mulan PSL v2 at:
*          http://license.coscl.org.cn/MulanPSL2
*
* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
* EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
* MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
*
* See the Mulan PSL v2 for more details.
***************************************************************************************/

#include <isa.h>
#include "local-include/reg.h"
CSR csr = {.mstatus = 0xa00001800};

const char *regs[] = {
  "$0", "ra", "sp", "gp", "tp", "t0", "t1", "t2",
  "s0", "s1", "a0", "a1", "a2", "a3", "a4", "a5",
  "a6", "a7", "s2", "s3", "s4", "s5", "s6", "s7",
  "s8", "s9", "s10", "s11", "t3", "t4", "t5", "t6"
};

word_t csr_read(word_t addr) {
  word_t data = 0;
  // printf("csr_read: addr: " FMT_WORD " data: " ,addr );
  switch (addr)
  {
  case MSTATUS_ADDR:  data = csr.mstatus; break;
  case MTVEC_ADDR:    data = csr.mtvec; break;
  case MCAUSE_ADDR:   data = csr.mcause; break;
  case MEPC_ADDR:     data = csr.mepc; break;
  case MIP_ADDR:      data = csr.mip; break;
  case MIE_ADDR:      data = csr.mie; break;
  default: assert(0);
  }
  // printf(FMT_WORD "\n",data );
  return data;
}

void csr_write(word_t addr, word_t data) {
  // printf("csr_write: addr: " FMT_WORD " data: " FMT_WORD "\n",addr,data );
  switch (addr)
  {
  case MSTATUS_ADDR:  csr.mstatus = data; break;
  case MTVEC_ADDR:    csr.mtvec   = data; break;
  case MCAUSE_ADDR:   csr.mcause  = data; break;
  case MEPC_ADDR:     csr.mepc    = data; break;
  case MIP_ADDR:      csr.mip     = data; break;
  case MIE_ADDR:      csr.mie     = data; break;
  default: assert(0);
  }
  return;
}

void csr_set_bits(word_t addr, word_t mask) {
  // printf("csr_set_bits: addr: " FMT_WORD " mask: " FMT_WORD "\n",addr,mask );
  switch (addr)
  {
  case MSTATUS_ADDR:  csr.mstatus |= mask; break;
  case MTVEC_ADDR:    csr.mtvec   |= mask; break;
  case MCAUSE_ADDR:   csr.mcause  |= mask; break;
  case MEPC_ADDR:     csr.mepc    |= mask; break;
  case MIP_ADDR:      csr.mip     |= mask; break;
  case MIE_ADDR:      csr.mie     |= mask; break;
  default: assert(0);
  }
  return;
}

void csr_clear_bits(word_t addr, word_t mask) {
  // printf("csr_clear_bits: addr: " FMT_WORD " mask: " FMT_WORD "\n",addr,mask );
  switch (addr)
  {
  case MSTATUS_ADDR:  csr.mstatus &= ~mask; break;
  case MTVEC_ADDR:    csr.mtvec   &= ~mask; break;
  case MCAUSE_ADDR:   csr.mcause  &= ~mask; break;
  case MEPC_ADDR:     csr.mepc    &= ~mask; break;
  case MIP_ADDR:      csr.mip     &= ~mask; break;
  case MIE_ADDR:      csr.mie     &= ~mask; break;
  default: assert(0);
  }
  return;
}


void isa_reg_display() {
  printf("nemu:\n");
  for (int i = 0; i < 8; ++i) {
    for (int j = 0; j < 4; ++j) {
        printf("%-3s:0x%016lx\t",regs[i*4+j],cpu.gpr[i*4+j]);
    }
    printf("\n");
  }
}

word_t isa_reg_str2val(const char *s, bool *success) {
  if(strcmp("PC",s) == 0) {
    *success = true;
    return cpu.pc;
  } 
  int i;
  for (i = 0; i < 32; ++i) {
    if(strcmp(regs[i],s) == 0) {
      *success = true;
      break;
    }
  }
  if(i < 32) {
    return cpu.gpr[i];
  }
  else return 0;
}
